/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <math.h>
#include <limits>
#include <exception>
#include <pgl/igfx/GDIpInterface.h>
#include <pgl/algotools/MemStream.h>
#include "GDILeak.h"

#ifndef UNICODE
#define THROW_IF_GRAPHICS_NOT_OK( g )\
if (!g)\
{   CString s; s.Format(_T("Gdiplus failed at %s (%d)"),_T(__FILE__),__LINE__);\
    throw std::exception(s);\
}\
if (g->GetLastStatus() != Gdiplus::Ok)\
{ \
    CString s; s.Format(_T("Gdiplus failed at %s (%d)"),_T(__FILE__),__LINE__);\
    throw std::exception(s);\
}
#else
#define THROW_IF_GRAPHICS_NOT_OK( g )\
if (!g)\
{\
    throw L"Gdiplus failed";\
}\
if (g->GetLastStatus() != Gdiplus::Ok)\
{ \
    throw L"Gdiplus failed";\
}
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

namespace gfxinterface
{

CGDIpInterface::CGDIpInterface()
: m_pGraphics(NULL), m_pDash(NULL), m_iPointsSize(0), m_pPoints(0), m_pFont(NULL)
{}

CGDIpInterface::~CGDIpInterface()
{
	if(m_pFont)
	{
		
      delete m_pFont;
		m_pFont=NULL;
	};
}

void CGDIpInterface::BeginPaint(HDC hDC)
{	
   
   using namespace Gdiplus;

	CGfxInterface::BeginPaint(hDC);

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	m_lStates = SGdipStateStack();

	m_curState.m_curColor=Color(0,0,0);
	m_curState.m_eFillType = SGDIpState::SolidFill;
	m_curState.m_fillColor=Color(150,150,150);

	m_curState.m_sLinearGradient.m_cStart=Color(0,0,0);
	m_curState.m_sLinearGradient.m_cEnd=Color(255,255,255);
	m_curState.m_sLinearGradient.m_pStart=PointF(0,0);
	m_curState.m_sLinearGradient.m_pEnd=PointF(1,1);

	m_curState.m_sTexture.m_pBitmap = NULL;

	m_curState.m_shadowColor=Color(90,0,0,0);
	m_curState.m_lineWidth=1;
	GetDefaultLOGFONT(m_curState.m_font);
	m_curState.m_dash.clear();

	m_curState.m_eLayer=LayerBack;

	if (!(m_dwRenderOption & RenderOptionNoAntiAlias))
		m_pGraphics->SetSmoothingMode(SmoothingModeHighQuality);
	if (!(m_dwRenderOption & RenderOptionNoTextAntiAlias))
		m_pGraphics->SetTextRenderingHint(TextRenderingHintAntiAlias);
	if (!(m_dwRenderOption & RenderOptionNoBiCubicInterpolation))
		m_pGraphics->SetInterpolationMode(InterpolationModeHighQualityBicubic);

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::EndPaint()			
{	
   CGfxInterface::EndPaint();

	m_pGraphics = NULL;
	// testing current state...
	if (m_curState.m_eFillType == SGDIpState::TextureFill)
	{
		delete m_curState.m_sTexture.m_pBitmap;
	}

	if (m_pFont)
	{
		
      delete m_pFont;
		m_pFont=NULL;
	}

	m_lStates = SGdipStateStack();
};

void CGDIpInterface::PushState()
{	
   using namespace Gdiplus;
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	// saving state
	SGDIpState oldstate=m_curState;
	oldstate.m_pTransformMatrix = new Matrix;
	m_pGraphics->GetTransform(oldstate.m_pTransformMatrix);
	oldstate.m_gState=m_pGraphics->Save();

	if (oldstate.m_eFillType == SGDIpState::TextureFill)
		oldstate.m_sTexture.m_pBitmap = m_curState.m_sTexture.m_pBitmap->Clone(0,0, 
			m_curState.m_sTexture.m_pBitmap->GetWidth(),
			m_curState.m_sTexture.m_pBitmap->GetHeight(),
			m_curState.m_sTexture.m_pBitmap->GetPixelFormat());

	// adding to stack
	m_lStates.push(oldstate);

	// saving state in GDI+
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};

void CGDIpInterface::PopState()
{	
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	// testing current state...
	if (m_curState.m_eFillType == SGDIpState::TextureFill)
	{
        if (m_curState.m_sTexture.m_pBitmap)
    		delete m_curState.m_sTexture.m_pBitmap;
	}

	// restoring state
	m_curState=m_lStates.top();
	m_pGraphics->Restore(m_curState.m_gState);	
	m_pGraphics->SetTransform(m_curState.m_pTransformMatrix);

	// deleting matrix...
	if (m_curState.m_pTransformMatrix)
		delete m_curState.m_pTransformMatrix;
	
	// regenerate current font
	GenerateFont(false);	

	m_lStates.pop();

	///////////////////////////////////////////////////////////////////////////////////
	// dashing
    m_pDash.clear();
	if (!m_curState.m_dash.empty())
	{
        m_pDash=m_curState.m_dash;
	}

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};


void CGDIpInterface::DrawArrow(double tailx, double taily, double tipx, double tipy, double tailthickness, double  headthickness, double headlength, bool filled)
{
   
	using namespace Gdiplus;
    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	static const int nArrowPoints = 7;
	static PointF pArrowPoints[nArrowPoints];
	static GraphicsContainer graphicsContainer;

	double bbtailx = WorldToBBx(tailx);
	double bbtaily = WorldToBBy(taily);
	double bbtipx = WorldToBBx(tipx);
	double bbtipy = WorldToBBy(tipy);
	double length = sqrt((bbtipy-bbtaily)*(bbtipy-bbtaily) + (bbtipx-bbtailx)*(bbtipx-bbtailx));
	double angle = atan( (tipy-taily)/(tipx-tailx));

	pArrowPoints[0].X=0;
	pArrowPoints[0].Y=static_cast<REAL>(-tailthickness/2.0);

	pArrowPoints[1].X=static_cast<REAL>(length);
	pArrowPoints[1].Y=static_cast<REAL>(-tailthickness/2.0);

	pArrowPoints[2].X=static_cast<REAL>(length);
	pArrowPoints[2].Y=static_cast<REAL>(-headthickness/2.0);

	pArrowPoints[3].X=static_cast<REAL>(length+headlength);
	pArrowPoints[3].Y=0;

	pArrowPoints[4].X=static_cast<REAL>(length);
	pArrowPoints[4].Y=static_cast<REAL>(headthickness/2.0);

	pArrowPoints[5].X=static_cast<REAL>(length);
	pArrowPoints[5].Y=static_cast<REAL>(tailthickness/2.0);

	pArrowPoints[6].X=0;
	pArrowPoints[6].Y=static_cast<REAL>(tailthickness/2.0);


	graphicsContainer = m_pGraphics->BeginContainer();

	m_pGraphics->SetSmoothingMode(SmoothingModeHighQuality);
	m_pGraphics->TranslateTransform(static_cast<REAL>(bbtailx),static_cast<REAL>(bbtaily));
	m_pGraphics->RotateTransform(static_cast<REAL>(angle*180));


	if (filled)
	{	
		brush_shared_ptr pBrush(GetCurrentBrush());
		m_pGraphics->FillClosedCurve(pBrush.get(), pArrowPoints, nArrowPoints, FillModeWinding, 0);
	}

	Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
	// Drawing
	m_pGraphics->DrawClosedCurve(&pen, pArrowPoints, nArrowPoints, 0);

	m_pGraphics->EndContainer(graphicsContainer);

    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawArrowAbs(double tailx, double taily, double dtipx, double dtipy, double tailthickness, double  headthickness, double headlength, bool filled)
{
	using namespace Gdiplus;
    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	static const int nArrowPoints = 7;
	static PointF pArrowPoints[nArrowPoints];
	static GraphicsContainer graphicsContainer;

	double bbtailx = WorldToBBx(tailx);
	double bbtaily = WorldToBBy(taily);
	double bbtipx = WorldToBBx(tailx+dtipx);
	double bbtipy = WorldToBBy(taily+dtipy);
	double length = sqrt((bbtipy-bbtaily)*(bbtipy-bbtaily) + (bbtipx-bbtailx)*(bbtipx-bbtailx));
	double angle = atan( (dtipy)/(dtipx));

	pArrowPoints[0].X=0;
	pArrowPoints[0].Y=static_cast<REAL>(-tailthickness/2.0);

	pArrowPoints[1].X=static_cast<REAL>(length);
	pArrowPoints[1].Y=static_cast<REAL>(-tailthickness/2.0);

	pArrowPoints[2].X=static_cast<REAL>(length);
	pArrowPoints[2].Y=static_cast<REAL>(-headthickness/2.0);

	pArrowPoints[3].X=static_cast<REAL>(length+headlength);
	pArrowPoints[3].Y=0;

	pArrowPoints[4].X=static_cast<REAL>(length);
	pArrowPoints[4].Y=static_cast<REAL>(headthickness/2.0);

	pArrowPoints[5].X=static_cast<REAL>(length);
	pArrowPoints[5].Y=static_cast<REAL>(tailthickness/2.0);

	pArrowPoints[6].X=0;
	pArrowPoints[6].Y=static_cast<REAL>(tailthickness/2.0);


	graphicsContainer = m_pGraphics->BeginContainer();

	m_pGraphics->SetSmoothingMode(SmoothingModeHighQuality);
	m_pGraphics->TranslateTransform(static_cast<REAL>(bbtailx),static_cast<REAL>(bbtaily));
	m_pGraphics->RotateTransform(static_cast<REAL>(angle*180));


		if (filled)
		{	
			brush_shared_ptr pBrush(GetCurrentBrush());
			m_pGraphics->FillClosedCurve(pBrush.get(), pArrowPoints, nArrowPoints, FillModeWinding, 0);
		}

		Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
		// Drawing
		m_pGraphics->DrawClosedCurve(&pen, pArrowPoints, nArrowPoints, 0);

	m_pGraphics->EndContainer(graphicsContainer);

    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawLine(double x1, double y1, double x2, double y2)
{
	using namespace Gdiplus;
    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
	REAL x=static_cast<REAL>(WorldToBBx(x1));
	m_pGraphics->DrawLine(&pen, 
		x, 
		static_cast<REAL>(WorldToBBy(y1)), 
		static_cast<REAL>(WorldToBBx(x2)), 
		static_cast<REAL>(WorldToBBy(y2))
		);
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawLineStrip(const CVectorF& vX, const CVectorF& vY, DWORD eLineTypeFlag, double dTension )
{
	using namespace Gdiplus;

    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	static GraphicsContainer graphicsContainer;

	INT nReal;
	size_t nStart;
	
	size_t n = __min(vX.size(),vY.size());

	if (n<2)
		return;

	if ( eLineTypeFlag & LineTypeSnapBottom )
	{
		// adding bottom if filled
		nReal=n+2;
		nStart=1;
		ResizePoints(nReal);

		m_pPoints[0].X=static_cast<REAL>(WorldToBBx(vX[0]));
		m_pPoints[0].Y=static_cast<REAL>(GetBBClipBottom());

		m_pPoints[n+1].X=static_cast<REAL>(WorldToBBx(vX[n-1]));
		m_pPoints[n+1].Y=static_cast<REAL>(GetBBClipBottom());
	}
	else
	{
		nReal=n;
		nStart=0;
		ResizePoints(nReal);
	}

	// creating point array
	for (size_t i=nStart;i<n+nStart;++i)
	{
		m_pPoints[i].X = static_cast<REAL>( WorldToBBx(vX[i-nStart]));
		m_pPoints[i].Y = static_cast<REAL>( WorldToBBy(vY[i-nStart]));
	}

	if (eLineTypeFlag & LineTypeFilled)
	{
		brush_shared_ptr pBrush(GetCurrentBrush());
		m_pGraphics->FillClosedCurve(
            pBrush.get(), 
            &(m_pPoints[0]),
            nReal, 
            Gdiplus::FillModeWinding, 
            (REAL)(dTension)
            );
	}

	// contour
	if ( !(eLineTypeFlag & LineTypeNoContour) )
	{
		// shadowing of line
		if (eLineTypeFlag & LineTypeShadow)
		{
			graphicsContainer = m_pGraphics->BeginContainer();
				m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
				m_pGraphics->TranslateTransform(m_curState.m_lineWidth,m_curState.m_lineWidth);
				Pen shadow(m_curState.m_shadowColor, m_curState.m_lineWidth+1);
	
				if (eLineTypeFlag & LineTypeClosed)	
				{
					// Drawing
					m_pGraphics->DrawClosedCurve(
                        &shadow, 
                        &(m_pPoints[0]), 
                        nReal, 
                        (REAL)(dTension)
                        );
				}
				else
				{
					if (dTension)
						m_pGraphics->DrawCurve(
                        &shadow, 
                        &(m_pPoints[0]), 
                        nReal, 
                        (REAL)(dTension)
                        );
					else
						m_pGraphics->DrawLines(&shadow, &(m_pPoints[0]), nReal);
				}
			m_pGraphics->EndContainer(graphicsContainer);
		}

		Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);

		// closed line
		if (eLineTypeFlag & LineTypeClosed && n>2)	
		{
			// Drawing
			m_pGraphics->DrawClosedCurve(&pen, &(m_pPoints[0]), nReal, (REAL)(dTension));
		}
		// open line
		else
		{
			if (dTension)
				m_pGraphics->DrawCurve(&pen, &(m_pPoints[0]), nReal, (REAL)(dTension));
			else
				m_pGraphics->DrawLines(&pen, &(m_pPoints[0]), nReal);
		}

	}

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawStepStrip(const CVectorF& vX, const CVectorF& vY, DWORD eLineTypeFlag )
{
	using namespace Gdiplus;
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	GraphicsContainer graphicsContainer;

	INT nReal;
	size_t nStart;

	size_t n = __min(vX.size(), vY.size());

	if (n==0)
		return;

	if ( eLineTypeFlag & LineTypeSnapBottom )
	{
		// adding bottom if filled
		nReal=2*n;
		nStart=1;
		ResizePoints(nReal);

		m_pPoints[0].X=static_cast<REAL>(WorldToBBx(vX[0]));
		m_pPoints[0].Y=static_cast<REAL>(GetBBClipBottom());

		m_pPoints[2*n-1].X=static_cast<REAL>(WorldToBBx(vX[n-1]));
		m_pPoints[2*n-1].Y=static_cast<REAL>(GetBBClipBottom());
	}
	else
	{
		nReal=2*n-2;
		nStart=0;
		ResizePoints(nReal);
	}

	// creating point array
	for (size_t i=0;i<n-1 ;++i)
	{
		m_pPoints[2*i+nStart].X = static_cast<REAL>(WorldToBBx(vX[i]));
		m_pPoints[2*i+nStart].Y = static_cast<REAL>(WorldToBBy(vY[i]));

		m_pPoints[2*i+1+nStart].X = static_cast<REAL>(WorldToBBx(vX[i+1]));
		m_pPoints[2*i+1+nStart].Y = static_cast<REAL>(WorldToBBy(vY[i]));
	}

	if (eLineTypeFlag & LineTypeFilled)
	{
		brush_shared_ptr pBrush(GetCurrentBrush());
		m_pGraphics->FillClosedCurve(pBrush.get(), &(m_pPoints[0]), nReal, Gdiplus::FillModeWinding, 0);
	}

	if ( !(eLineTypeFlag & LineTypeNoContour) )
	{
		// shadowing of line
		if (eLineTypeFlag & LineTypeShadow)
		{
			graphicsContainer = m_pGraphics->BeginContainer();
				m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
				m_pGraphics->TranslateTransform(m_curState.m_lineWidth,m_curState.m_lineWidth);
				Pen shadow(m_curState.m_shadowColor, m_curState.m_lineWidth+1);
	
				if (eLineTypeFlag & LineTypeClosed)	
				{
					// Drawing
					m_pGraphics->DrawClosedCurve(&shadow, &(m_pPoints[0]), nReal, 0);
				}
				else
				{
					m_pGraphics->DrawLines(&shadow, &(m_pPoints[0]), nReal);
				}
			m_pGraphics->EndContainer(graphicsContainer);
		}

		Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
		if (eLineTypeFlag & LineTypeClosed)	
		{
			// Drawing
			m_pGraphics->DrawClosedCurve(&pen, &(m_pPoints[0]), nReal, 0);
		}
		else
		{
			m_pGraphics->DrawLines(&pen, &(m_pPoints[0]), nReal);
		}
	}

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawMultipleLineStrip(int ns, const CVectorF& vX, const CVectorF& vY, DWORD eLineTypeFlag  )
{
	using namespace Gdiplus;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	INT nscur, index;
	INT nAddedPoints;
	size_t nStart, nscurend;

	size_t n=__min(vX.size(), vY.size());
	static GraphicsContainer graphicsContainer;
	Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
	Pen shadow(m_curState.m_shadowColor, m_curState.m_lineWidth+1);
	brush_shared_ptr pBrush(GetCurrentBrush());

	if (n==0)
		return;


	if ( eLineTypeFlag & LineTypeSnapBottom )
	{
		// adding bottom if filled
		nAddedPoints=2;
		nStart=1;
	}
	else
	{
		nAddedPoints=0;
		nStart=0;
	}
	ResizePoints(ns+nAddedPoints);

	index=0;
	nscur=0;
	do
	{
		nscur=__min(ns,(int)n-index);
		nscurend=index+nscur+nStart;
		for (size_t i=index+nStart;i<nscurend;++i)
		{
			m_pPoints[i-index].X = static_cast<REAL>( WorldToBBx(vX[i-nStart]) );
			m_pPoints[i-index].Y = static_cast<REAL>( WorldToBBy(vY[i-nStart]) );
		}
		// adding bottom
		if (eLineTypeFlag & LineTypeSnapBottom)
		{
			m_pPoints[0].X=static_cast<REAL>(WorldToBBx(vX[index]));
			m_pPoints[0].Y=static_cast<REAL>(GetBBClipBottom());

			m_pPoints[nscur+1].X=static_cast<REAL>(WorldToBBx(vX[index+nscur-1]));
			m_pPoints[nscur+1].Y=static_cast<REAL>(GetBBClipBottom());
		}
	
		// draw filled
		if (eLineTypeFlag & LineTypeFilled)
		{
			m_pGraphics->FillClosedCurve(pBrush.get(), &(m_pPoints[0]), nscur+nAddedPoints, Gdiplus::FillModeWinding, 0);
		}
	
		// draw contour
		if ( !(eLineTypeFlag & LineTypeNoContour) )
		{
			// shadowing of line
			if (eLineTypeFlag & LineTypeShadow)
			{
				graphicsContainer = m_pGraphics->BeginContainer();
					m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
					m_pGraphics->TranslateTransform(m_curState.m_lineWidth,m_curState.m_lineWidth);
		
					if (eLineTypeFlag & LineTypeClosed)	
					{
						// Drawing
						m_pGraphics->DrawClosedCurve(&shadow, &(m_pPoints[0]), nscur+nAddedPoints, 0);
					}
					else
					{
						m_pGraphics->DrawLines(&shadow, &(m_pPoints[0]), nscur+nAddedPoints);
					}
				m_pGraphics->EndContainer(graphicsContainer);
			}

			if (eLineTypeFlag & LineTypeClosed)	
			{
				// Drawing
				m_pGraphics->DrawClosedCurve(&pen, &(m_pPoints[0]), nscur+nAddedPoints, 0);
			}
			else
			{
				m_pGraphics->DrawLines(&pen, &(m_pPoints[0]), nscur+nAddedPoints);
			}
		}

		// udpating index...
		index+=ns;
	} while (index< (int)n);

	if (eLineTypeFlag & LineTypeFilled)
	{
		Gdiplus::SolidBrush brush(m_curState.m_fillColor);
		m_pGraphics->FillClosedCurve(&brush, &(m_pPoints[0]), nscur+nAddedPoints, Gdiplus::FillModeWinding, 0);
	}

	if ( !(eLineTypeFlag & LineTypeNoContour) )
	{
		Gdiplus::Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
		if (eLineTypeFlag & LineTypeClosed)	
		{
			// Drawing
			m_pGraphics->DrawClosedCurve(&pen, &(m_pPoints[0]), nscur+nAddedPoints, 0);
		}
		else
		{
			m_pGraphics->DrawLines(&pen, &(m_pPoints[0]), nscur+nAddedPoints);
		}
	}
	
	// cleaning
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawBandStrip(const CVectorF& vX, const CVectorF& vYBottom,const CVectorF& vYTop, DWORD eBandTypeFlag, double dTension)
{
   using namespace Gdiplus;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	static GraphicsContainer graphicsContainer;
	
	size_t n = __min(vX.size(),__min(vYBottom.size(), vYTop.size()));

	if (n==0)
		return; 

	ResizePoints(2*n);

	// creating point array
	if (eBandTypeFlag & BandTypeAbsolute)
	{
		for (size_t i=0;i<n;++i)
		{
			m_pPoints[i].X = m_pPoints[2*n-1-i].X = static_cast<REAL>(WorldToBBx(vX[i]));
			m_pPoints[i].Y = static_cast<REAL>(WorldToBBy(vYTop[i]));
			m_pPoints[2*n-1-i].Y = static_cast<REAL>(WorldToBBy(vYBottom[i]));
		}
	}
	else
	{
		for (size_t i=0;i<n;++i)
		{
			m_pPoints[i].X = m_pPoints[2*n-1-i].X = static_cast<REAL>( WorldToBBx(vX[i]) );
			m_pPoints[2*n-1-i].Y = static_cast<REAL>(WorldToBBy(vYBottom[i]));
			m_pPoints[i].Y = static_cast<REAL>(WorldToBBy(vYBottom[i] + vYTop[i]));
		}
	}

	// filling
	if ( !(eBandTypeFlag & BandTypeNotFilled) )
	{
		brush_shared_ptr pBrush(GetCurrentBrush());
		m_pGraphics->FillClosedCurve(pBrush.get(), &(m_pPoints[0]), 2*n, Gdiplus::FillModeWinding, (REAL)dTension);
	}

	// drawing line
	if ( !(eBandTypeFlag & BandTypeNoContour) )
	{
		Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
		if (eBandTypeFlag & BandTypeFullContour)	
		{
			// shadowing of line
			if (eBandTypeFlag & BandTypeLineShadow)
			{
				graphicsContainer = m_pGraphics->BeginContainer();
					m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
					m_pGraphics->TranslateTransform(m_curState.m_lineWidth,m_curState.m_lineWidth);
					Pen shadow(m_curState.m_shadowColor, m_curState.m_lineWidth+1);
	
					m_pGraphics->DrawClosedCurve(&shadow, &(m_pPoints[0]), 2*n, (REAL)dTension);
				m_pGraphics->EndContainer(graphicsContainer);
			}

			// Drawing
			m_pGraphics->DrawClosedCurve(&pen, &(m_pPoints[0]), 2*n, 0);
		}
		else if (eBandTypeFlag & BandTypeLowerLineOnly)	
		{
			// shadowing of line
			if (eBandTypeFlag & BandTypeLineShadow)
			{
				graphicsContainer = m_pGraphics->BeginContainer();
					m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
					m_pGraphics->TranslateTransform(m_curState.m_lineWidth,m_curState.m_lineWidth);
					Pen shadow(m_curState.m_shadowColor, m_curState.m_lineWidth+1);
					if (dTension)
						m_pGraphics->DrawCurve(&shadow, &(m_pPoints[n]), n, (REAL)dTension);
					else
						m_pGraphics->DrawLines(&shadow, &(m_pPoints[n]), n);
				m_pGraphics->EndContainer(graphicsContainer);
			}

			m_pGraphics->DrawLines(&pen, &(m_pPoints[n]), n);
		}
		else if (eBandTypeFlag & BandTypeUpperLowerLine)	
		{
			// shadowing of line
			if (eBandTypeFlag & BandTypeLineShadow)
			{
				graphicsContainer = m_pGraphics->BeginContainer();
					m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
					m_pGraphics->TranslateTransform(m_curState.m_lineWidth,m_curState.m_lineWidth);
					Pen shadow(m_curState.m_shadowColor, m_curState.m_lineWidth+1);
	
					if (dTension)
					{
						m_pGraphics->DrawCurve(&shadow, &(m_pPoints[0]), n, (REAL)dTension);
						m_pGraphics->DrawCurve(&shadow, &(m_pPoints[n]), n, (REAL)dTension);
					}
					else
					{
						m_pGraphics->DrawLines(&shadow, &(m_pPoints[0]), n);
						m_pGraphics->DrawLines(&shadow, &(m_pPoints[n]), n);
					}
				m_pGraphics->EndContainer(graphicsContainer);
			}

			if (dTension)
			{
				m_pGraphics->DrawCurve(&pen, &(m_pPoints[0]), n, static_cast<REAL>(dTension));
				m_pGraphics->DrawCurve(&pen, &(m_pPoints[n]), n, static_cast<REAL>(dTension));
			}
			else
			{	
				m_pGraphics->DrawLines(&pen, &(m_pPoints[0]), n);
				m_pGraphics->DrawLines(&pen, &(m_pPoints[n]), n);
			}
		}
		else
		{
			// shadowing of line
			if (eBandTypeFlag & BandTypeLineShadow)
			{
				graphicsContainer = m_pGraphics->BeginContainer();
					m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
					m_pGraphics->TranslateTransform(m_curState.m_lineWidth,m_curState.m_lineWidth);
					Pen shadow(m_curState.m_shadowColor, m_curState.m_lineWidth+1);
					if (dTension)
						m_pGraphics->DrawCurve(&shadow, &(m_pPoints[0]), n, (REAL)dTension);
					else
						m_pGraphics->DrawLines(&shadow, &(m_pPoints[0]), n);
				m_pGraphics->EndContainer(graphicsContainer);
			}
			
			if (dTension)
				m_pGraphics->DrawCurve(&pen, &(m_pPoints[0]), n, (REAL)dTension);
			else
				m_pGraphics->DrawLines(&pen, &(m_pPoints[0]), n);
		}
	}

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawErrorBars( const CVectorF& vXs, const CVectorF& vYs, const CVectorF& vUps, const CVectorF& vDowns, double dWidth, bool bAbsolute, bool bVertical)
{
   using namespace Gdiplus;
	size_t n;
	
	if (bAbsolute)
	{
		if (bVertical)
			n=__min(vXs.size(), __min(vUps.size(), vDowns.size()) );
		else
			n=__min(vYs.size(), __min(vUps.size(), vDowns.size()) );
	}
	else
		n=__min(vXs.size(), __min(vYs.size(), __min(vUps.size(), vDowns.size()) ) );

	if (n==0)
		return;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	// getting penup
	Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
	size_t i;
	REAL wx, wy, wUp, wDown, wLeft, wRight;

	if (!bAbsolute && bVertical)
	{
		for (i=0;i<n;++i)
		{
			wx=static_cast<REAL>(WorldToBBx(vXs[i]));
			wUp=static_cast<REAL>(WorldToBBy(vYs[i]+vUps[i]));
			wDown=static_cast<REAL>(WorldToBBy(vYs[i]-vDowns[i]));

			// drawing error bar
			m_pGraphics->DrawLine(&pen,
				static_cast<REAL>(wx-dWidth/2.0),
				static_cast<REAL>(wDown),
				static_cast<REAL>(wx+dWidth/2.0),
				static_cast<REAL>(wDown));
			m_pGraphics->DrawLine(&pen,
				wx,
				static_cast<REAL>(wDown),
				wx,
				static_cast<REAL>(wUp));
			m_pGraphics->DrawLine(&pen,
				static_cast<REAL>(wx-dWidth/2.0),
				static_cast<REAL>(wUp),
				static_cast<REAL>(wx+dWidth/2.0),
				static_cast<REAL>(wUp));
		}
	}
	else if( !bAbsolute && ! bVertical)
	{
		for (i=0;i<n;++i)
		{
			wy=static_cast<REAL>(WorldToBBy(vYs[i]));
			wLeft=static_cast<REAL>(WorldToBBx(vXs[i]-vDowns[i]));
			wRight=static_cast<REAL>(WorldToBBx(vXs[i]+vUps[i]));

			// drawing error bar
			m_pGraphics->DrawLine(&pen,
				wLeft,static_cast<REAL>(wy+dWidth/2.0),
				wLeft,static_cast<REAL>(wy-dWidth/2.0));
			m_pGraphics->DrawLine(&pen,
				wLeft,static_cast<REAL>(wy),
				wRight,static_cast<REAL>(wy));
			m_pGraphics->DrawLine(&pen,
				wRight,static_cast<REAL>(wy+dWidth/2.0),
				wRight,static_cast<REAL>(wy-dWidth/2.0));
		}
	}
	else if (bAbsolute && !bVertical)
	{
		for (i=0;i<n;++i)
		{
			wy=static_cast<REAL>(WorldToBBy(vYs[i]));
			wLeft=static_cast<REAL>(WorldToBBx(vDowns[i]));
			wRight=static_cast<REAL>(WorldToBBx(vUps[i]));

			// drawing error bar
			m_pGraphics->DrawLine(&pen,
				wLeft,static_cast<REAL>(wy+dWidth/2.0),
				wLeft,static_cast<REAL>(wy-dWidth/2.0));
			m_pGraphics->DrawLine(&pen,
				wLeft,static_cast<REAL>(wy),
				wRight,static_cast<REAL>(wy));
			m_pGraphics->DrawLine(&pen,
				wRight,static_cast<REAL>(wy+dWidth/2.0),
				wRight,static_cast<REAL>(wy-dWidth/2.0));
		}
	}
	else // bAbsolute && bVertical
	{
		for (i=0;i<n;++i)
		{
			wx=static_cast<REAL>(WorldToBBx(vXs[i]));
			wUp=static_cast<REAL>(WorldToBBy(vUps[i]));
			wDown=static_cast<REAL>(WorldToBBy(vDowns[i]));

			// drawing error bar
			m_pGraphics->DrawLine(&pen,
				static_cast<REAL>(wx-dWidth/2.0),static_cast<REAL>(wDown),
				static_cast<REAL>(wx+dWidth/2.0),static_cast<REAL>(wDown));
			m_pGraphics->DrawLine(&pen,
				wx,static_cast<REAL>(wDown),
				wx,static_cast<REAL>(wUp));
			m_pGraphics->DrawLine(&pen,
				static_cast<REAL>(wx-dWidth/2.0),static_cast<REAL>(wUp),
				static_cast<REAL>(wx+dWidth/2.0),static_cast<REAL>(wUp));
		}
	}

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};

void CGDIpInterface::DrawStock( double x0, double dx, const CVectorF& vHis, const CVectorF& vLows, const CVectorF& vOpens, const CVectorF& vCloses, const SRGBA& cDownColor, DWORD eStockTypeFlag)
{
	
   using namespace Gdiplus;

	size_t n = __min(vHis.size(), __min( vCloses.size(), __min( vOpens.size(), vLows.size()) ) );
	size_t i;
	double x;
	REAL wx, wx1, ulx,uly, wclose,wopen, wwidth, wheight, wlow, whigh;
	
	if (n<2)
		return;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	// getting penup
	Pen penup(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(penup);

	// configuration pendown
	Color colDown(
		static_cast<BYTE>(floor(cDownColor.a*255)),
		static_cast<BYTE>(floor(cDownColor.r*255)),
		static_cast<BYTE>(floor(cDownColor.g*255)),
		static_cast<BYTE>(floor(cDownColor.b*255))
		);
	Pen pendown(colDown, m_curState.m_lineWidth); SetCurPenState(pendown);

	// configurating brushes for candlestick
	SolidBrush brushup(m_curState.m_curColor);
	SolidBrush White(Color(255,255,255));
	SolidBrush brushdown(colDown);
	
	// width of rect
	wwidth=static_cast<REAL>(WorldToBBAbsx(dx)-4);
	wwidth=std::max(2.f,wwidth);

	switch(eStockTypeFlag)
	{
	case StockTypeCandleStick:
		x=x0;
		// drawing the rest
		for (i=0;i<n;++i)
		{
			// udpating x
			wx=static_cast<REAL>(WorldToBBx(x));
			ulx=wx-wwidth/2;
			wclose=static_cast<REAL>(WorldToBBy(vCloses[i]));
			wopen=static_cast<REAL>(WorldToBBy(vOpens[i]));
			wlow=static_cast<REAL>(WorldToBBy(vLows[i]));
			whigh=static_cast<REAL>(WorldToBBy(vHis[i]));
			
			// drawing white
			wheight=static_cast<REAL>(fabs(wclose-wopen));
			if (vCloses[i] > vOpens[i])
			{
				uly=wclose;
				// fill rectangle
				m_pGraphics->FillRectangle(&White, ulx, uly, wwidth, wheight);
			}
			else
			{
				uly=wopen;
				// fill rectangle
				m_pGraphics->FillRectangle(&brushdown, ulx, uly, wwidth, wheight);
			}
				
			m_pGraphics->DrawRectangle(&penup, ulx, uly, wwidth, wheight);
			// draw lines
			m_pGraphics->DrawLine(&penup, wx, wlow, wx, uly+wheight);				
			m_pGraphics->DrawLine(&penup, wx, uly, wx, whigh);

			// updating x
			x+=dx;
		}
		break;
	case StockTypeOpenHighLowClose:
		x=x0-dx/2;
		wx=static_cast<REAL>(WorldToBBx(x));
		x=x0;
		wx1=static_cast<REAL>(WorldToBBx(x));
		wclose=static_cast<REAL>(WorldToBBy(vCloses[0]));
		wopen=static_cast<REAL>(WorldToBBy(vOpens[0]));
		
		// drawing first
		m_pGraphics->DrawLine(&penup, 
			wx, static_cast<REAL>(WorldToBBy(vLows[0])), 
			wx, static_cast<REAL>(WorldToBBy(vHis[0]))
			);	
		m_pGraphics->DrawLine(&penup, wx, wclose,
			wx1, wopen);		
		wx=wx1;
		x+=dx/2;
		wx1=static_cast<REAL>(WorldToBBx(x));
		m_pGraphics->DrawLine(&penup, wx, wopen,
			wx1, wclose);		
		
		// drawing the rest
		for (i=1;i<n;++i)
		{
			// udpating x
			wx=wx1;
			x+=dx/2;
			wx1=static_cast<REAL>(WorldToBBx(x));
			wclose=static_cast<REAL>(WorldToBBy(vCloses[i]));
			wopen=static_cast<REAL>(WorldToBBy(vOpens[i]));
			
			if (vCloses[i] > vCloses[i-1])
			{
				m_pGraphics->DrawLine(&penup, wx, wopen,
					wx1, wopen);		
				
				m_pGraphics->DrawLine(&penup, 
					wx1, static_cast<REAL>(WorldToBBy(vLows[i])), 
					wx1, static_cast<REAL>(WorldToBBy(vHis[i]))
					);
				
				wx=wx1;
				x+=dx/2;
				wx1=static_cast<REAL>(WorldToBBx(x));
				
				m_pGraphics->DrawLine(&penup, wx, wclose,
					wx1, wclose);		
			}
			else
			{
				m_pGraphics->DrawLine(&pendown, wx, wopen,
					wx1, wopen);
				
				m_pGraphics->DrawLine(&pendown, 
					wx1, static_cast<REAL>(WorldToBBy(vLows[i])), 
					wx1, static_cast<REAL>(WorldToBBy(vHis[i]))
					);
				
				wx=wx1;
				x+=dx/2;
				wx1=static_cast<REAL>(WorldToBBx(x));
				
				m_pGraphics->DrawLine(&pendown, wx, wclose,
					wx1, wclose);		
			}
		}
		break;
	}
	
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};

void CGDIpInterface::DrawGridHor(double llx, double urx, double lly, double ury,double dy)
{
   using namespace Gdiplus;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	Gdiplus::Pen pen(m_curState.m_curColor, m_curState.m_lineWidth);
	SetCurPenState(pen);

    if (algotools::IsEqual(dy,0.0))
        throw std::exception("Horizontal grid step is too close to zero");

	size_t n = (size_t)floor( (ury-lly)/dy)+1;
	for (size_t i=0;i<n;++i)
	{
		m_pGraphics->DrawLine(&pen,
			static_cast<REAL>( WorldToBBx(llx) ),
			static_cast<REAL>( WorldToBBy(lly+i*dy) ),
			static_cast<REAL>( WorldToBBx(urx) ),
			static_cast<REAL>( WorldToBBy(lly+i*dy) )
			);
	}
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawGridVer(double llx, double urx, double lly, double ury,double dx)
{
   THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	Gdiplus::Pen pen(m_curState.m_curColor, m_curState.m_lineWidth);
	SetCurPenState(pen);

    if (algotools::IsEqual(dx,0.0))
        throw std::exception("Vertical grid step is too close to zero");


	size_t n = static_cast<size_t>(floor( (urx-llx)/dx)+1);
	float x;

	for (size_t i=0;i<n;++i)
	{
		x=static_cast<float>( WorldToBBx(llx+i*dx) );
		m_pGraphics->DrawLine(&pen,
			x,
			static_cast<float>( WorldToBBy(lly) ),
			x,
			static_cast<float>( WorldToBBy(ury)) 
			);
	}
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawRect(double llx, double lly, double urx, double ury, DWORD dwRectFlag, SPointD sShadowSize)
{
   using namespace Gdiplus;
	using namespace std;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	// Set the colors of the points in the array.
	int count = 4;
	static Color colors[4];
	static PointF ptsF[4];	

	double bllx=WorldToBBx(llx);
	double blly=WorldToBBy(lly);
	double burx=WorldToBBx(urx);
	double bury=WorldToBBy(ury);

	if ( dwRectFlag & RectTypeFilled )
	{
		brush_shared_ptr pBrush(GetCurrentBrush());
		m_pGraphics->FillRectangle(pBrush.get(), (REAL)bllx,(REAL)bury,
			(REAL)WorldToBBAbsx(urx-llx),(REAL)WorldToBBAbsy(ury-lly));
	}

	if ( !( dwRectFlag & RectTypeNoContour) )
	{
		Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
		m_pGraphics->DrawRectangle(&pen, (REAL)bllx,(REAL)bury,
			(REAL)WorldToBBAbsx(urx-llx),(REAL)WorldToBBAbsy(ury-lly));
	}

	// drawing shadows...
	double dx=fabs(sShadowSize.x);
	double dy=fabs(sShadowSize.y);

	if (dwRectFlag & RectTypeShadowLowerRight)
	{
		// border colors
		for (int i=0;i<3;++i)
		{
			if (i<3)
				colors[i]=Color(50,
					m_curState.m_shadowColor.GetRed(),
					m_curState.m_shadowColor.GetGreen(),
					m_curState.m_shadowColor.GetBlue());
			else
				colors[i]=Color(255,
					m_curState.m_shadowColor.GetRed(),
					m_curState.m_shadowColor.GetGreen(),
					m_curState.m_shadowColor.GetBlue());
		}

		// points
		ptsF[0].X = (REAL)(bllx+dx);		ptsF[0].Y = (REAL)(blly);
		ptsF[1].X = (REAL)(bllx+dx);		ptsF[1].Y = (REAL)(blly+dy);
		ptsF[2].X = (REAL)(burx+dx+1);		ptsF[2].Y = (REAL)(blly+dy);
		ptsF[3].X = (REAL)(burx+1);			ptsF[3].Y = (REAL)(blly);

		// Use the array of points to construct a path.
		GraphicsPath path1;
		path1.AddLines(ptsF, count);
		path1.CloseFigure();
		PathGradientBrush pthGrBrush1(ptsF, count);
		pthGrBrush1.SetSurroundColors(colors, &count);
		pthGrBrush1.SetCenterColor(m_curState.m_shadowColor);
		pthGrBrush1.SetGammaCorrection(TRUE);

		// points
		ptsF[0].X = (REAL)(burx+dx);	ptsF[0].Y = (REAL)(blly+dy);
		ptsF[1].X = (REAL)(burx+dx);	ptsF[1].Y = (REAL)(bury+dy);
		ptsF[2].X = (REAL)(burx);		ptsF[2].Y = (REAL)(bury+dy);
		ptsF[3].X = (REAL)(burx);		ptsF[3].Y = (REAL)(blly);

		// Use the array of points to construct a path.
		GraphicsPath path2;
		path2.AddLines(ptsF, count);
		path2.CloseFigure();
		PathGradientBrush pthGrBrush2(ptsF, count);
		pthGrBrush2.SetSurroundColors(colors, &count);
		pthGrBrush2.SetCenterColor(m_curState.m_shadowColor);
		pthGrBrush2.SetGammaCorrection(TRUE);

		// Fill the path with the path gradient brush.
		m_pGraphics->FillPath(&pthGrBrush1, &path1);
		m_pGraphics->FillPath(&pthGrBrush2, &path2);
	}

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawRectStrip( const CVectorF& vXUL, const CVectorF& vYUL, const CVectorF& vWidth, const CVectorF& vHeight, DWORD dwRectFlag)
{
	using namespace Gdiplus;
	RectF* pRects;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	size_t n = __min( vXUL.size(), __min( vYUL.size(), __min( vWidth.size(), vHeight.size() ) ) );
	if (n==0)
		return;

	pRects = new RectF[n];

	for (size_t i = 0;i<n;++i)
	{
		pRects[i] = RectF( vXUL[i], vYUL[i], vWidth[i],vHeight[i] );
	}

	if ( dwRectFlag & RectTypeFilled )
	{
		brush_shared_ptr pBrush(GetCurrentBrush());
		m_pGraphics->FillRectangles(pBrush.get(), pRects,(INT)n);
	}

	if ( !( dwRectFlag & RectTypeNoContour) )
	{
		Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);

		m_pGraphics->DrawRectangles(&pen, pRects,(INT)n);
	}

	delete pRects;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawCircle(double cx, double cy, double radius, DWORD eLineType)
{
   using namespace Gdiplus;
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	RectF rect((REAL)(WorldToBBx(cx)-radius), (REAL)(WorldToBBy(cy)-radius), (REAL)(2*radius), (REAL)(2*radius));

	if (eLineType & LineTypeFilled)
	{
		brush_shared_ptr pBrush(GetCurrentBrush());
		m_pGraphics->FillEllipse(pBrush.get(),rect);
	}
	
	if ( !(eLineType & LineTypeNoContour))
	{
		Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
		m_pGraphics->DrawEllipse(&pen, rect);
	}
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};

void CGDIpInterface::DrawPolygon(double cx, double cy,  const CVectorF& vPx, const CVectorF& vPy, DWORD eLineType)
{
   using namespace Gdiplus;
	size_t i;
	float wcx,wcy;
	const size_t uSideCount = __min(vPx.size(), vPy.size());

	if (uSideCount < 3)
		return;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	PointF* pPolygon=new PointF[uSideCount];
	if (!pPolygon)
		return;

	wcx=(float)WorldToBBx(cx);
	wcy=(float)WorldToBBy(cy);

	for (i=0;i<uSideCount;++i)
	{
		pPolygon[i].X=(REAL)(wcx+vPx[i]);
		pPolygon[i].Y=(REAL)(wcy-vPy[i]);
	}

	if (eLineType & LineTypeFilled)
	{
		brush_shared_ptr pBrush(GetCurrentBrush());
		m_pGraphics->FillPolygon(pBrush.get(),pPolygon,(INT)uSideCount, Gdiplus::FillModeWinding);
	}

	if ( !(eLineType & LineTypeNoContour))
	{
		Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
		if (eLineType & LineTypeClosed)
			m_pGraphics->DrawPolygon(&pen, pPolygon,(INT)uSideCount);
		else
			m_pGraphics->DrawLines(&pen, pPolygon,(INT)uSideCount);
	}

	if (pPolygon)
		delete pPolygon;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};

void CGDIpInterface::DrawCircleStrip(const CVectorF& vCx, const CVectorF& vCy, double radius, DWORD eLineType)
{
   using namespace Gdiplus;
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	const size_t n = __min(vCx.size(), vCy.size());
	if (n==0)
		return;

	RectF rect(0,0,(REAL)(2*radius),(REAL)(2*radius));

	if (eLineType & LineTypeFilled)
	{
		brush_shared_ptr pBrush(GetCurrentBrush());
		for (size_t i=0;i<n;++i)
		{
			rect.X=(REAL)(WorldToBBx(vCx[i])-radius);
			rect.Y=(REAL)(WorldToBBy(vCy[i])-radius);
			m_pGraphics->FillEllipse(pBrush.get(),rect);
		}
	}
	
	if (!(eLineType & LineTypeNoContour))
	{
		Gdiplus::Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
		for (size_t i=0;i<n;++i)
		{
			rect.X=(REAL)(WorldToBBx(vCx[i])-radius);
			rect.Y=(REAL)(WorldToBBy(vCy[i])-radius);
			m_pGraphics->DrawEllipse(&pen, rect);
		}
	}

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};

void CGDIpInterface::DrawPolygonStrip(const CVectorF& vCx, const CVectorF& vCy,  const CVectorF& vPx, const CVectorF& vPy, DWORD eLineType)
{
   using namespace Gdiplus;
	size_t i,ic;
	REAL wcx,wcy;
	const size_t n = __min(vCx.size(), vCy.size());
	const size_t uSideCount = __min( vPx.size(), vPy.size());

	if (uSideCount <3 || n==0)
		return;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	// making polygon...
	PointF* pPolygon=new PointF[uSideCount];
	if (!pPolygon)
		return;

	if (eLineType & LineTypeFilled)
	{
		brush_shared_ptr pBrush(GetCurrentBrush());
		for (i=0;i<n;++i)
		{
			// computing polygon
			wcx=(REAL)WorldToBBx(vCx[i]);
			wcy=(REAL)WorldToBBy(vCy[i]);
			for (ic=0;ic<uSideCount;++ic)
			{
				pPolygon[ic].X=wcx+vPx[ic];
				pPolygon[ic].Y=wcy-vPy[ic];
			}

			m_pGraphics->FillPolygon(pBrush.get(),pPolygon, (INT)uSideCount);
		}
	}

	if (! (eLineType & LineTypeNoContour))
	{
		Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
		for (i=0;i<n;++i)
		{
			// computing polygon
			wcx=(REAL)WorldToBBx(vCx[i]);
			wcy=(REAL)WorldToBBy(vCy[i]);
			for (ic=0;ic<uSideCount;++ic)
			{
				pPolygon[ic].X=wcx+vPx[ic];
				pPolygon[ic].Y=wcy-vPy[ic];
			}

			if (eLineType & LineTypeClosed)
				m_pGraphics->DrawPolygon(&pen,pPolygon, (INT)uSideCount);
			else
				m_pGraphics->DrawLines(&pen,pPolygon, (INT)uSideCount);
		}
	}

	if (pPolygon)
		delete pPolygon;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};


void CGDIpInterface::DrawSurf(
    const contour::TLattice<float>& tLattice, 
    const CVectorF& vColor, 
    DWORD dwFlag
    )
{
   using namespace Gdiplus;
	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	int sideCount=tLattice.GetFaceSideSize();
	size_t i;
	int k;
	contour::TQuadGridLattice<float>::VertexContainer vPos;
	contour::TQuadGridLattice<float>::IndexContainer vIndex;

	double r,g,b,xc,yc,angle;
	PointF psF[4];
	Color colors[4];
	Color center;
	// getting pen
	Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);

	for (i=0;i<tLattice.GetFaceSize();++i)
	{
		tLattice.GetFace(i,vIndex,vPos);

		ASSERT(vIndex.size() == vPos.size());
		ASSERT(vIndex.size() <= 4);
		r=g=b=xc=yc=0;
		for (k=0; k<sideCount; ++k)
		{
			// position
			psF[k].X=(REAL)WorldToBBx( vPos[k].first );
			psF[k].Y=(REAL)WorldToBBy( vPos[k].second );
			// color
			colors[k]= Color( (INT)floor(vColor[vIndex[k]*3]*255), 
				(INT)floor(vColor[vIndex[k]*3+1]*255),
				(INT)floor(vColor[vIndex[k]*3+2]*255));
			r+=vColor[vIndex[k]*3];
			g+=vColor[vIndex[k]*3+1];
			b+=vColor[vIndex[k]*3+2];
			xc+=psF[k].X;
			yc+=psF[k].Y;
		}
		

		xc/=sideCount;
		yc/=sideCount;
		for (k=0;k<sideCount;++k)
		{
			angle=atan2( (psF[k].Y-yc) , (psF[k].X-xc) );
			if ( (psF[k].X-xc) < 0)
				angle+=IGFX_PI;
			psF[k].X+=(REAL)(1.5*cos(angle));
			psF[k].Y+=(REAL)(1.5*sin(angle));
		}

		center = Color((INT)floor(r/sideCount*255.0),(INT)floor(g/sideCount*255.0),(INT)floor(b/sideCount*255.0));

		PathGradientBrush brush(psF,sideCount);
		brush.SetSurroundColors(colors, &sideCount);	
		brush.SetCenterColor(center);

		m_pGraphics->FillPolygon(&brush,psF,sideCount);
		if (dwFlag & SurfWireFrame)
			m_pGraphics->DrawPolygon(&pen,psF,sideCount);
	}

/*	if (dwFlag & ESurfWireFrame)
	{
		DrawGridHor(llx, llx+(nx-1)*dx, uly-(ny-1)*dy, uly,dy);
		DrawGridVer(llx, llx+(nx-1)*dx, uly-(ny-1)*dy, uly,dx);
	}
*/
    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};

void CGDIpInterface::PushClipWorld(double llx, double lly, double urx, double ury)
{
   THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

    CGfxInterface::PushClipWorld(llx,lly,urx,ury);
    Gdiplus::RectF clipRectF((Gdiplus::REAL)GetBBClipLeft(),(Gdiplus::REAL)GetBBClipTop(),
        (Gdiplus::REAL)GetBBClipWidth(),(Gdiplus::REAL)GetBBClipHeight());  
    m_pGraphics->SetClip(clipRectF);	

    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};

void CGDIpInterface::PopClipWorld()
{	
   
   THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

    CGfxInterface::PopClipWorld();
    if (m_stClips.empty())
    {
        m_pGraphics->ResetClip();	
    }
    else
    {
        Gdiplus::RectF clipRectF((Gdiplus::REAL)GetBBClipLeft(),(Gdiplus::REAL)GetBBClipTop(),
            (Gdiplus::REAL)GetBBClipWidth(),(Gdiplus::REAL)GetBBClipHeight());  
        m_pGraphics->SetClip(clipRectF);	
    }
    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};


void CGDIpInterface::DrawVectorField(const contour::TLattice<float>& tLattice, const CVectorF& vVx, const CVectorF& vVy, float fScale, DWORD dwFlags)
{
	using namespace std;
	using namespace Gdiplus;
	using namespace contour;
	static double tol=numeric_limits<float>::denorm_min()*numeric_limits<float>::denorm_min();

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	size_t i;
	PointF p[3];
	REAL x,y,dx,dy, norm;
	Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
	if (!(dwFlags & VectorFieldNoStartCap))
		pen.SetStartCap(LineCapRoundAnchor);
	if (!(dwFlags & VectorFieldNoEndCap))
		pen.SetEndCap(LineCapArrowAnchor);
	
	const size_t n=__min( tLattice.GetVertexSize(), __min( vVx.size(), vVy.size()));
	for (i=0;i<n;++i)
	{
		norm=vVx[i]*vVx[i] + vVy[i]*vVy[i];
		if ( norm < tol )
			continue;

		if (dwFlags & VectorFieldConstantLength)
		{
			norm=(REAL)sqrt(norm);
			dx=vVx[i]*fScale/norm*20;
			dy=vVy[i]*fScale/norm*20;
		}
		else
		{
			dx=static_cast<REAL>(WorldToBBAbsx(vVx[i]*fScale));
			dy=static_cast<REAL>(WorldToBBAbsx(vVy[i]*fScale));
		}

		x=static_cast<REAL>(WorldToBBx(tLattice.GetXVertex(i)));
		y=static_cast<REAL>(WorldToBBy(tLattice.GetYVertex(i)));
		// drawing vertex
		m_pGraphics->DrawLine(&pen, x, y, x+dx, y+dy);
	}


    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawText(double x, double y, LPCTSTR str, ETextAlignment ta, double angle)
{
   using namespace Gdiplus;
	USES_CONVERSION;
    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	int nChar=_tcsclen(str);
	if (nChar == 0)
		return;

	GenerateFont();
	SolidBrush  solidBrush(m_curState.m_curColor);
	PointF      pointF((REAL)(WorldToBBx(x)), (REAL)(WorldToBBy(y)));
	StringFormat sf;
	static RectF boundingBox;
	LPCWSTR wStr;

	// measuring string
	wStr=T2CW(str);

	m_pGraphics->MeasureString(wStr,nChar,m_pFont, pointF,&boundingBox);
	VERIFY(m_pGraphics->GetLastStatus() == Ok);

//	Pen black( Color(0,0,0),1);

	angle = NormalizeAngle(angle);

	if (angle==0)
	{
		boundingBox.Y-=boundingBox.Height;
		switch(ta)
		{
		case  TextAlignmentRight:
			sf.SetAlignment(StringAlignmentFar);
			boundingBox.X-=boundingBox.Width;
			break;
		case  TextAlignmentCenter:
			sf.SetAlignment(StringAlignmentCenter);
			boundingBox.X-=boundingBox.Width/2;
			break;
		}
		m_pGraphics->DrawString(wStr, -1, m_pFont, boundingBox, &sf,&solidBrush);
//		m_pGraphics->DrawRectangle(&black,boundingBox);
	}
	else
	{
		Matrix matrix;
		m_pGraphics->GetTransform(&matrix);
		Matrix* pMatrix=matrix.Clone();
		pMatrix->RotateAt((REAL)(-angle), PointF(boundingBox.X, boundingBox.Y));
		m_pGraphics->SetTransform(pMatrix);

		boundingBox.Y-=boundingBox.Height;
		switch(ta)
		{
		case  TextAlignmentRight:
			sf.SetAlignment(StringAlignmentFar);
			boundingBox.X-=boundingBox.Width;
			break;
		case  TextAlignmentCenter:
			sf.SetAlignment(StringAlignmentCenter);
			boundingBox.X-=boundingBox.Width/2;
			break;
		}
		m_pGraphics->DrawString(wStr, -1, m_pFont, boundingBox, &sf,&solidBrush);
//		m_pGraphics->DrawRectangle(&black,boundingBox);
		if (pMatrix)
			delete pMatrix;

		m_pGraphics->SetTransform(&matrix);
	}
    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawTextStrip(const std::vector<SPointD>& vPoints, const std::vector<std::_tstring>& vStrings, ETextAlignment ta, double angle)
{
   using namespace Gdiplus;
	USES_CONVERSION;

    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	GenerateFont();
	SolidBrush  solidBrush(m_curState.m_curColor);
	PointF      pointF;
	RectF		boundingbox;
	StringFormat sf;
	double offset=0;
	static RectF boundingBox;
	int nChar;
	LPCWSTR wStr;
	size_t i;
    const size_t n = std::min(vPoints.size(),vStrings.size());
	angle = NormalizeAngle(angle);

	// text alignement
	switch(ta)
	{
	case  TextAlignmentRight:
		sf.SetAlignment(Gdiplus::StringAlignmentFar);
		offset=1;
		break;
	case  TextAlignmentCenter:
		sf.SetAlignment(Gdiplus::StringAlignmentCenter);
		offset=0.5;
		break;
	}

	if (angle==0)
	{
		for (i=0;i<n;++i)
		{
			nChar=vStrings[i].size();
			if (nChar==0)
				continue;

			wStr=T2CW(vStrings[i].c_str());

			pointF.X=static_cast<REAL>((WorldToBBx(vPoints[i].x)));
			pointF.Y=static_cast<REAL>((WorldToBBy(vPoints[i].y)));

			m_pGraphics->MeasureString(wStr,nChar,m_pFont, pointF,&boundingBox);
			VERIFY(m_pGraphics->GetLastStatus() == Gdiplus::Ok);
	
			boundingBox.X-=static_cast<REAL>((boundingBox.Width*offset));
			boundingBox.Y-=static_cast<REAL>((boundingBox.Height));

			m_pGraphics->DrawString(wStr, -1, m_pFont, boundingBox, &sf,&solidBrush);
		}
	}
	else
	{
		Matrix curMatrix;
		m_pGraphics->GetTransform(&curMatrix);
		// rotated
		for (i=0;i<n;++i)
		{
			nChar=vStrings[i].size();
			if (nChar==0)
				continue;

			wStr=T2CW(vStrings[i].c_str());

			pointF.X=static_cast<REAL>((WorldToBBx(vPoints[i].x)));
			pointF.Y=static_cast<REAL>((WorldToBBy(vPoints[i].y)));

			m_pGraphics->MeasureString(wStr,nChar,m_pFont, pointF,&boundingBox);
			VERIFY(m_pGraphics->GetLastStatus() == Gdiplus::Ok);

			Matrix* pMatrix=curMatrix.Clone();
			pMatrix->RotateAt((REAL)(-angle), PointF(boundingBox.X, boundingBox.Y));
			m_pGraphics->SetTransform(pMatrix);

			boundingBox.X-=static_cast<REAL>((boundingBox.Width*offset));
			boundingBox.Y-=static_cast<REAL>((boundingBox.Height));

			m_pGraphics->DrawString(wStr, -1, m_pFont, boundingBox, &sf,&solidBrush);
			if (pMatrix)
				delete pMatrix;
		}
		m_pGraphics->SetTransform(&curMatrix);
	}

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};

void CGDIpInterface::SetCurPenState(Gdiplus::Pen &pen)
{
	if (!m_curState.m_dash.empty())
	{
		pen.SetDashPattern( &(m_pDash[0]), m_curState.m_dash.size());
	}
}

void CGDIpInterface::SetDash(LPCTSTR dash)
{	
	using namespace Gdiplus;
	m_curState.m_dash.clear();

	// no dashing
	if (dash == _T(""))
	{
        m_pDash.clear();
		m_curState.m_dash.clear();
		return;
	}

	// parsing file...
	std::_tistringstream istr(dash);	
	int length;

	while ( !istr.eof())
	{
		istr>>length;
		m_curState.m_dash.push_back(static_cast<REAL>(length));
	}	

    m_pDash=m_curState.m_dash;
};


void CGDIpInterface::DrawArc(double cnx, double cny, double rn, double angle, double aperture, double rep, DWORD flags)
{
	using namespace Gdiplus;
	GraphicsContainer container;

    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	// invalid flag
	if ( ! ( (flags &  ArcTypeContour) || (flags &  ArcTypeFill) ) )
		return;

	PointF point( (REAL)(GetBBClipLeft() + cnx*GetBBClipWidth()) , (REAL)(GetBBClipTop() + cny*GetBBClipHeight()) );
	const REAL radius = (REAL)(__min(rn*GetBBClipWidth(),rn*GetBBClipHeight()));
	
	angle=NormalizeAngle(angle);
	double anglerad=-IGFX_PI*angle/180.0;
	double aperturerad=-IGFX_PI*aperture/180.0;

	// moving if exploded
	if (flags & ArcTypeExploded)
	{
		point.X+=(REAL)(rep*cos(anglerad+aperturerad/2.0));
		point.Y+=(REAL)(rep*sin(anglerad+aperturerad/2.0));
	}

	// creating path
	GraphicsPath path;
	path.AddLine(
		static_cast<REAL>((radius*cos(anglerad+aperturerad))) , 
		static_cast<REAL>((radius*sin(anglerad+aperturerad))), 
		static_cast<REAL>(0), 
		static_cast<REAL>(0)
		);
	path.AddLine(
		static_cast<REAL>(0), 
		static_cast<REAL>(0), 
		static_cast<REAL>((radius*cos(anglerad))), 
		static_cast<REAL>((radius*sin(anglerad))));
	path.AddArc((REAL)-radius , 
		static_cast<REAL>(-radius), 
		static_cast<REAL>(2*radius), 
		static_cast<REAL>(2*radius),
		static_cast<REAL>(-angle), 
		static_cast<REAL>(-aperture));
	path.CloseFigure();

	container = m_pGraphics->BeginContainer();
		m_pGraphics->SetSmoothingMode(SmoothingModeAntiAlias);
		m_pGraphics->TranslateTransform(point.X,point.Y);

		// drawing contour...
		if (flags & ArcTypeFill)
		{
			Gdiplus::SolidBrush brush(m_curState.m_fillColor);
			m_pGraphics->FillPath(&brush,&path);
		}
	
		if (flags & ArcTypeContour)
		{
			Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
			m_pGraphics->DrawPath(&pen, &path);
		}
	m_pGraphics->EndContainer(container);

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawDonnut(double cnx, double cny, double rin, double rout, double angle, double aperture, double rep, DWORD flags)
{
	using namespace Gdiplus;
	GraphicsContainer container;

    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	// invalid flag
	if ( ! ( (flags &  ArcTypeContour) || (flags &  ArcTypeFill) ) )
		return;

	PointF point( 
		static_cast<REAL>(GetBBClipLeft() + cnx*GetBBClipWidth()) , 
		static_cast<REAL>(GetBBClipTop() + cny*GetBBClipHeight()) 
		);
	const REAL radiusIn = static_cast<REAL>(std::min(rin*GetBBClipWidth(),rin*GetBBClipHeight()));
	const REAL radiusOut = static_cast<REAL>(std::min(rout*GetBBClipWidth(),rout*GetBBClipHeight()));
	
	angle=NormalizeAngle(angle);
	aperture=NormalizeAngle(aperture);

	double anglerad=-IGFX_PI*angle/180.0;
	double aperturerad=-IGFX_PI*aperture/180.0;

	// moving if exploded
	if (flags & ArcTypeExploded)
	{
		point.X+=static_cast<REAL>(rep*cos(anglerad+aperturerad/2.0));
		point.Y+=static_cast<REAL>(rep*sin(anglerad+aperturerad/2.0));
	}

	// creating path
	GraphicsPath path;
	path.AddLine( 
		static_cast<REAL>(radiusOut*cos(anglerad+aperturerad)) , 
		static_cast<REAL>(radiusOut*sin(anglerad+aperturerad)), 
		static_cast<REAL>(radiusIn*cos(anglerad+aperturerad)) , 
		static_cast<REAL>(radiusIn*sin(anglerad+aperturerad))
		);
	path.AddArc(-radiusIn , 
		-radiusIn, 
		2*radiusIn, 
		2*radiusIn,
		(REAL)(-angle-aperture), 
		(REAL)(aperture));
	path.AddLine((REAL)(radiusIn*cos(anglerad)), (REAL)(radiusIn*sin(anglerad)), 
		(REAL)(radiusOut*cos(anglerad)), (REAL)(radiusOut*sin(anglerad)));
	path.AddArc(-radiusOut , 
		-radiusOut, 
		2*radiusOut, 
		2*radiusOut,
		static_cast<REAL>(-angle), 
		static_cast<REAL>(-aperture));
	path.CloseFigure();

	container = m_pGraphics->BeginContainer();
		m_pGraphics->SetSmoothingMode(SmoothingModeAntiAlias);
		m_pGraphics->TranslateTransform(point.X,point.Y);

		// drawing contour...
		if (flags & ArcTypeFill)
		{
			Gdiplus::SolidBrush brush(m_curState.m_fillColor);
			m_pGraphics->FillPath(&brush,&path);
		}
	
		if (flags & ArcTypeContour)
		{
			Pen pen(m_curState.m_curColor, m_curState.m_lineWidth); SetCurPenState(pen);
			m_pGraphics->DrawPath(&pen, &path);
		}
	m_pGraphics->EndContainer(container);

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}

void CGDIpInterface::DrawBitmap(  Gdiplus::Bitmap*  pBitmap, const SRectI& rOrigBitmap, const SRectD& rDestBitmap)
{
   using namespace Gdiplus;

    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

    if (!pBitmap)
        throw std::exception("Bitmap is null");
    if (pBitmap->GetLastStatus()!=Gdiplus::Ok)
        throw std::exception("Bitmap status is not ok");

	m_pGraphics->DrawImage( pBitmap, 
		(REAL)rDestBitmap.left, (REAL)rDestBitmap.top,
		(REAL)rDestBitmap.width, (REAL)rDestBitmap.height);

    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
}


void CGDIpInterface::ResizePoints(size_t iNewSize)
{
	if (iNewSize>m_iPointsSize)
	{
		m_iPointsSize=iNewSize;
		m_pPoints.resize(m_iPointsSize);
	}
}

void CGDIpInterface::SetLinearGradientFill(const SPointD& point1, const SPointD& point2, const SRGBA& col1, const SRGBA& col2)
{	
	using namespace Gdiplus;

	m_curState.m_sLinearGradient.m_cStart=Color(
		static_cast<BYTE>(floor(col1.a*255)),
		static_cast<BYTE>(floor(col1.r*255)),
		static_cast<BYTE>(floor(col1.g*255)),
		static_cast<BYTE>(floor(col1.b*255))
		);
	m_curState.m_sLinearGradient.m_cEnd=Color(
		static_cast<BYTE>(floor(col2.a*255)),
		static_cast<BYTE>(floor(col2.r*255)),
		static_cast<BYTE>(floor(col2.g*255)),
		static_cast<BYTE>(floor(col2.b*255))
		);
	m_curState.m_sLinearGradient.m_pStart=PointF( 
		static_cast<REAL>(WorldToBBx(point1.x)), 
		static_cast<REAL>(WorldToBBy( point1.y))
		);
	m_curState.m_sLinearGradient.m_pEnd=PointF( 
		static_cast<REAL>(WorldToBBx(point2.x)), 
		static_cast<REAL>(WorldToBBy( point2.y))
		);
	m_curState.m_eFillType = SGDIpState::LinearGradientFill;
};

void CGDIpInterface::SetTextureFill( Gdiplus::Bitmap* pBitmap, const SRectD& rDestRect, ETextureWrapMode eWrapMode)
{
	using namespace Gdiplus;
	
    if (!pBitmap)
        throw std::exception("Bitmap is null");
    if (pBitmap->GetLastStatus()!=Gdiplus::Ok)
        throw std::exception("Bitmap status is not ok");

	// testing current state...
	if (m_curState.m_eFillType == SGDIpState::TextureFill)
	{
		if (m_curState.m_sTexture.m_pBitmap)
			delete m_curState.m_sTexture.m_pBitmap;
		m_curState.m_sTexture.m_pBitmap=NULL;
	}

	m_curState.m_sTexture.m_pBitmap = pBitmap->Clone(0,0,
		pBitmap->GetWidth(),
		pBitmap->GetHeight(),
		pBitmap->GetPixelFormat());

	RectF rect;

	rect.X=static_cast<REAL>(rDestRect.left);
	rect.Y=static_cast<REAL>(rDestRect.top);
	rect.Width=static_cast<REAL>(rDestRect.width);
	rect.Height=static_cast<REAL>(rDestRect.height);

	if(rect.Height<0)
        throw std::exception("Bitmap height is negative ?");
    if(rect.Width<0)
        throw std::exception("Bitmap width is negative ?");
	
	m_curState.m_sTexture.m_rDestRect = rect;
	m_curState.m_sTexture.m_eWrapMode = GetWrapMode(eWrapMode);
	m_curState.m_eFillType = SGDIpState::TextureFill;
}


CGDIpInterface::brush_shared_ptr CGDIpInterface::GetCurrentBrush()
{
	using namespace Gdiplus;
    typedef boost::shared_ptr<LinearGradientBrush> linear_gradient_brush_shared_ptr;
    
    linear_gradient_brush_shared_ptr pLinearGradientBrush;

	switch( m_curState.m_eFillType)
	{
	case SGDIpState::SolidFill:
		return brush_shared_ptr(new SolidBrush(m_curState.m_fillColor));
	case SGDIpState::LinearGradientFill:
		pLinearGradientBrush = linear_gradient_brush_shared_ptr(
                new LinearGradientBrush(m_curState.m_sLinearGradient.m_pStart,
			        m_curState.m_sLinearGradient.m_pEnd,
			        m_curState.m_sLinearGradient.m_cStart,
			        m_curState.m_sLinearGradient.m_cEnd
                )
            );
		pLinearGradientBrush->SetGammaCorrection(true);
		return pLinearGradientBrush;

	case SGDIpState::TextureFill:
	    THROW_IF_GRAPHICS_NOT_OK(m_curState.m_sTexture.m_pBitmap);
		return brush_shared_ptr(
            new TextureBrush( 
                m_curState.m_sTexture.m_pBitmap, 
                m_curState.m_sTexture.m_eWrapMode, 
                m_curState.m_sTexture.m_rDestRect
                )
            ); 
	default:
        throw std::exception("Unknown brush type");
		return brush_shared_ptr();
	}
}

Gdiplus::WrapMode CGDIpInterface::GetWrapMode(ETextureWrapMode eWrapMode)
{
	switch(eWrapMode)
	{
	case TexWrapModeTileFlipX:
		return Gdiplus::WrapModeTileFlipX;
	case TexWrapModeTileFlipY:
		return Gdiplus::WrapModeTileFlipX;
	case TexWrapModeTileFlipXY:
		return Gdiplus::WrapModeTileFlipXY;
	case TexWrapModeClamp:
		return Gdiplus::WrapModeClamp;
	default:
		return Gdiplus::WrapModeTile;
	}
}


void CGDIpInterface::Rotate( double angleRad, double xOrig, double yOrig)
{
	using namespace Gdiplus;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	m_pGraphics->TranslateTransform(
		static_cast<REAL>(WorldToBBx(xOrig)),
		static_cast<REAL>(WorldToBBy(yOrig))
		);
	m_pGraphics->RotateTransform(static_cast<REAL>(-angleRad*180.0/IGFX_PI));
	m_pGraphics->TranslateTransform(
		static_cast<REAL>(-WorldToBBx(xOrig)), 
		static_cast<REAL>(-WorldToBBy(yOrig))
		);

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};

void CGDIpInterface::Scale( double xScale, double yScale, double xOrig, double yOrig)
{
   using namespace Gdiplus;

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);

	m_pGraphics->TranslateTransform(
		static_cast<REAL>(WorldToBBx(xOrig)),
		static_cast<REAL>(WorldToBBy(yOrig))
		);
	m_pGraphics->ScaleTransform(
		static_cast<REAL>(xScale), 
		static_cast<REAL>(yScale)
		);
	m_pGraphics->TranslateTransform(
		static_cast<REAL>(-WorldToBBx(xOrig)), 
		static_cast<REAL>(-WorldToBBy(yOrig))
		);

	THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
};

void CGDIpInterface::SetFont(const LOGFONT& logFont)
{
   if ( memcmp(&m_curState.m_font,&logFont, sizeof(LOGFONT)) != 0)
	{
		m_curState.m_font=logFont;
		// regenerating font
		if (m_pFont)
		{
			
         delete m_pFont;
			m_pFont = NULL;
		}
	}
	GenerateFont();
}	

void CGDIpInterface::GenerateFont(bool bForceGeneration)
{ 
	using namespace Gdiplus;

	if (bForceGeneration)
	{
		
      delete m_pFont;
		m_pFont = NULL;
	}


	if (!m_pFont)
	{
      if (!m_pGraphics)
         throw std::exception("graphics is null");
		// setting size of font
		long height=m_curState.m_font.lfHeight;
      HDC hdc = GetHDC();
      BOOL newHDC = FALSE;
      if (!hdc)
      {
         hdc = m_pGraphics->GetHDC();
         newHDC = TRUE;
      }

      int fontSize = CreateFontSize(hdc, height);
      m_curState.m_font.lfHeight=-static_cast<long>(ceil(fontSize+0.0));

#if defined ( _DEBUG )
#undef new
#endif
      
		m_pFont=new Font(hdc, &m_curState.m_font);
      
		if(!m_pFont)
            throw std::exception("could not create font");

#if defined( _DEBUG )
   #define new DEBUG_NEW
#endif
		m_curState.m_font.lfHeight=height;
      if(newHDC)
      {
         m_pGraphics->ReleaseHDC(hdc);
      }

	    THROW_IF_GRAPHICS_NOT_OK(m_pGraphics);
	}
};

//TODO: Figure out dynamic casting issue
void CGDIpInterface::PushClipWorldD(double llx, double lly, double urx, double ury)
{
   CGfxInterface::PushClipWorld(llx,lly,urx,ury);
}
void CGDIpInterface::PopClipWorldD()
{
   CGfxInterface::PopClipWorld();
}

} // end namespace gfxinterface
